﻿
#region Copyright & License
// --------------------------
// Copyright 2008 Daniel Gray
// --------------------------
// This file is part of Kale.
//
// Kale is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Kale is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Kale.  If not, see <http://www.gnu.org/licenses/>.
#endregion
#region File details
// --------------------------------------------------------
// Filename          : SessionProvider.cs
// Author            : Daniel Gray
// Created           : 05/05/2008
// --------------------------------------------------------
#endregion
            
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kale.Core.Security;
using Kale.Core.Configuration;
using System.Data.Linq;

namespace Kale.Db.Sql
{
   [RequiresConnectionString]
   public class SessionProvider : BaseSqlProvider, ISessionProvider
   {
      private string _anonymousUsername;
      private CacheExpirationType _expirationType;
      private int _expirationMinutes;
      private SessionSerializer _serializer;

      /// <summary>
      /// Initializes a new instance of the <see cref="SqlSecurityProvider"/> class.
      /// </summary>
      /// <param name="configurationDictionary">The configuration dictionary.</param>
      public SessionProvider(Dictionary<string, string> configurationDictionary)
      {
         LoadSqlConfigurationValuesFromDictionary(configurationDictionary);

         if (!configurationDictionary.ContainsKey(SessionProviderElement.AnonymousUsernameConfigurationName) ||
            string.IsNullOrEmpty(configurationDictionary[SessionProviderElement.AnonymousUsernameConfigurationName]))
         {
            throw CreateMissingConfigurationValueException(SessionProviderElement.AnonymousUsernameConfigurationName);
         }
         else
         {
            _anonymousUsername = configurationDictionary[SessionProviderElement.AnonymousUsernameConfigurationName];
         }

         if (!configurationDictionary.ContainsKey(SessionProviderElement.ExpirationTypeConfigurationName) ||
            string.IsNullOrEmpty(configurationDictionary[SessionProviderElement.ExpirationTypeConfigurationName]))
         {
            throw CreateMissingConfigurationValueException(SessionProviderElement.ExpirationTypeConfigurationName);
         }
         else
         {
            _expirationType = (CacheExpirationType)Enum.Parse(typeof(CacheExpirationType), configurationDictionary[SessionProviderElement.ExpirationTypeConfigurationName]);
         }

         if (!configurationDictionary.ContainsKey(SessionProviderElement.ExpirationMinutesConfigurationName) ||
            string.IsNullOrEmpty(configurationDictionary[SessionProviderElement.ExpirationMinutesConfigurationName]))
         {
            throw CreateMissingConfigurationValueException(SessionProviderElement.ExpirationMinutesConfigurationName);
         }
         else
         {
            _expirationMinutes = int.Parse(configurationDictionary[SessionProviderElement.ExpirationMinutesConfigurationName]);
         }

         _serializer = new SessionSerializer();
      }

      #region ISessionProvider Members

      //TODO:implement auto clean up for expired sessions.

      public ISession Logon(string username, string password)
      {
         // hash password
         byte[] passwordHash = PasswordHelper.HashPassword(password);

         // lookup user and password
         using (KaleModelDataContext context = GetContext())
         {
            User u = (from s in context.Users where s.Name == username && s.Password == new Binary(passwordHash) select s).FirstOrDefault();

            if (u == null)
            {
               // no match
               return null;
            }
            else
            {
               // match - delete any existing sessions (for non-anonymous users) and create a new one.
               if (u.Name != _anonymousUsername)
               {
                  context.Sessions.DeleteAllOnSubmit(from s in context.Sessions where s.UserUid == u.Uid select s);
               }

               Session newSession = new Session();

               newSession.Uid = Guid.NewGuid();
               newSession.UserUid = u.Uid;
               newSession.Login = DateTime.Now;
               newSession.LastAccess = DateTime.Now;
               newSession.Expiry = GetSessionExpiry(newSession.Login, newSession.LastAccess);
               newSession.IsAuthenticated = u.Name != _anonymousUsername;

               context.Sessions.InsertOnSubmit(newSession);
               context.SubmitChanges();

               return newSession;
            }
         }
      }

      private DateTime GetSessionExpiry(DateTime login, DateTime lastAccess)
      {
         if (_expirationType == CacheExpirationType.Absolute)
         {
            return login.AddMinutes(_expirationMinutes);
         }
         else
         {
            return lastAccess.AddMinutes(_expirationMinutes);
         }
      }

      public void RemoveSession(Guid sessionUid)
      {
         using (KaleModelDataContext context = GetContext())
         {
            context.Sessions.DeleteAllOnSubmit(from s in context.Sessions where s.Uid == sessionUid select s);
            context.SubmitChanges();
         }
      }

      public ISession GetSession(Guid sessionUid)
      {
         using (KaleModelDataContext context = GetContext())
         {
            Session session = (from s in context.Sessions where s.Uid == sessionUid select s).FirstOrDefault();

            session.LastAccess = DateTime.Now;
            session.Expiry = GetSessionExpiry(session.Login, session.LastAccess);
            session.IsAuthenticated = session.User.Name != _anonymousUsername;

            context.SubmitChanges();

            _serializer.DeserializeSession(session, context);

            return session;
         }
      }

      public void SaveSession(ISession session)
      {
         if (session == null) throw new ArgumentNullException("session");

         using (KaleModelDataContext context = GetContext())
         {
            _serializer.SerializeSession(session as Session, context);
         }
      }

      #endregion

   }
}
